package studio.xiaoyun.web;

import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Collections;
import java.util.Date;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import javax.xml.bind.annotation.adapters.XmlAdapter;
import javax.xml.parsers.DocumentBuilderFactory;

import org.w3c.dom.Document;
import org.w3c.dom.Element;

import studio.xiaoyun.web.resource.Resource;

/**
 * 使用JAXB将Java类转换为xml时，解析List
 * @author 岳正灵
 * @since 1.0.0
 */
public class ListXMLAdapter extends XmlAdapter<Object, List<?>> {
	private SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

	@Override
	public List<?> unmarshal(Object doc) throws Exception {
		return Collections.emptyList();
	}

	@Override
	public Object marshal(List<?> list) throws Exception {
		Document doc = DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument();
		Element rows = doc.createElement("rows");
		marshalForList(doc, rows, list);
		doc.appendChild(rows);
		return rows;
	}

	@SuppressWarnings("rawtypes")
	private void marshalForList(Document doc, Element element, List list) throws Exception {
		for (Object obj : list) {
			if(obj==null){
				continue;
			}
			Element e = doc.createElement("array");
			setContent(doc,e,obj);
			element.appendChild(e);
		}
	}

	@SuppressWarnings("rawtypes")
	private void marshalForMap(Document doc, Element element, Map map) throws Exception {
		Iterator it = map.keySet().iterator();
		while(it.hasNext()){
			Object key = it.next();
			Object obj = map.get(key);
			if(obj==null){
        		continue;
        	}
        	Element e = doc.createElement(key.toString());
        	setContent(doc,e,obj);
			element.appendChild(e);
		}
	}

	private void marshalForArray(Document doc, Element element, Object array) throws Exception {
		int length = Array.getLength(array);
        for(int i=0;i<length;i++){
        	Object obj = Array.get(array, i);
        	if(obj==null){
        		continue;
        	}
        	Element e = doc.createElement("array");
        	setContent(doc,e,obj);
			element.appendChild(e);
        }
	}

	@SuppressWarnings("rawtypes")
	private void marshalForResource(Document doc, Element element, Resource resource) throws Exception {
		//找到类的所有属性
		List<Field> fieldList = new LinkedList<Field>();
		Field[] fields = resource.getClass().getDeclaredFields();
		fieldList.addAll(Arrays.asList(fields));
		Class superClass = resource.getClass().getSuperclass();
		while(superClass != Object.class && superClass != Resource.class){
			fields = superClass.getDeclaredFields();
			fieldList.addAll(Arrays.asList(fields));
			superClass = superClass.getSuperclass();
		}
		//找到类的所有公共的方法
		Method[] methods = resource.getClass().getMethods();
        for(Method method:methods){
        	String name = method.getName();
        	if(name.startsWith("get") && name.length()>4 && !name.equals("getClass")
        			&& method.getParameterCount()==0 && method.getReturnType()!=Void.TYPE ){
        		Object obj = method.invoke(resource);
            	if(obj==null){
            		continue;
            	}
            	String fieldName = name.substring(3,4).toLowerCase()+name.substring(4);
            	for(Field field:fieldList){
            		if(field.getName().equalsIgnoreCase(fieldName)){
            			fieldName = field.getName();
            			break;
            		}
            	}
            	Element e = doc.createElement(fieldName);
            	setContent(doc,e,obj);
    			element.appendChild(e);
        	}
        }
	}

	@SuppressWarnings("rawtypes")
	private void setContent(Document doc, Element element, Object obj) throws Exception {
		if (obj.getClass().isArray()) {
			marshalForArray(doc, element, obj);
		} else if (obj instanceof Map) {
			marshalForMap(doc, element, (Map) obj);
		} else if (obj instanceof List) {
			marshalForList(doc, element, (List) obj);
		} else if (obj instanceof Resource) {
			marshalForResource(doc, element, (Resource) obj);
		} else if (obj instanceof Date) {
			String dateStr = dateFormat.format((Date) obj);
			element.setTextContent(dateStr);
		} else {
			element.setTextContent(obj.toString());
		}
	}
}
